#include "interface.h"
#include "surface_affichable.h"
#include "observateur_evenement.h"
#include <algorithm>

extern const char creation_ecran[] = "Impossible de creer l'ecran";
extern const char chargement_police[] = "Impossible de charger la police";

class afficheur_surfaces {
	SDL_Surface *ecran_;
public:
	afficheur_surfaces(SDL_Surface *ecran) : ecran_(ecran) {}
	void operator()(surface_affichable *s) {
		if(!s->visible_)
			return;
		
		SDL_Rect p;
		p.x = s->x_; p.y = s->y_;
		SDL_BlitSurface(s->surface_, NULL, ecran_, &p);
	}
};

struct tri_selon_z {
	bool operator()(surface_affichable *gauche, surface_affichable *droite) {
		return gauche->z() < droite->z();
	}
};

void video::initialiser() {
	dependant_de<sdl> sdl_doit_etre_lance;
	if(SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
		throw une_erreur<initialisation>(true);
	
	if(TTF_Init() < 0)
		throw une_erreur<initialisation>(true);
	
	SDL_EnableUNICODE(SDL_ENABLE);
}

void video::fermer() {
	SDL_EnableUNICODE(SDL_DISABLE);
	TTF_Quit();
	SDL_QuitSubSystem(SDL_INIT_VIDEO);
}

module_interface::module_interface() throw(erreur) : doit_quitter_(false), doit_rafraichir_(false), police_normale_(TTF_OpenFont("droidsans.ttf", 12)), police_grosse_(TTF_OpenFont("droidsansbold.ttf", 24)) {
	// Ajuste le titre de la fenêtre
	SDL_WM_SetCaption("Mon piano d'amour", NULL);
	
	// Crée la surface d'écran
	ecran_ = SDL_SetVideoMode(326, 252, 0, SDL_SWSURFACE);
    if(!ecran_)
		throw une_erreur<creation_ecran>(true);
	
	if(!police_normale_)
		throw une_erreur<chargement_police>(true);
	
	if(!police_normale_)
		throw une_erreur<chargement_police>(true);
    
}

module_interface::~module_interface() throw() {
	TTF_CloseFont(police_normale_);
	TTF_CloseFont(police_grosse_);
}

void module_interface::rafraichir() {
	static ::afficheur_surfaces afficheur(ecran_);
	std::for_each(a_afficher_.begin(), a_afficher_.end(), afficheur);
	doit_rafraichir_ = false;
}

void module_interface::actualiser_affichage() {
	if(doit_rafraichir_)
		rafraichir();
	SDL_Flip(ecran_);
}

SDL_Surface *module_interface::generer_texte(std::string tt, bool gros) {
	static const SDL_Color couleurTexte = { 255, 255, 255 };
	return TTF_RenderText_Blended((gros ? police_grosse_ : police_normale_), tt.c_str(), couleurTexte);
}

void module_interface::inscrire(surface_affichable &s) {
	a_afficher_.push_back(&s);
	changement_ordre();
}

void module_interface::desinscrire(surface_affichable &s) {
	if(!a_afficher_.empty())
		a_afficher_.erase(std::find(a_afficher_.begin(), a_afficher_.end(), &s));
}

void module_interface::changement_ordre() {
	tri_selon_z mon_tri;
	std::sort(a_afficher_.begin(), a_afficher_.end(), mon_tri);
	
	demander_rafraichir();
}

void module_interface::inscrire(observateur_evenement &o) {
	a_notifier_.push_back(&o);
}

void module_interface::desinscrire(observateur_evenement &o) {
	if(!a_notifier_.empty())
		a_notifier_.erase(std::find(a_notifier_.begin(), a_notifier_.end(), &o));
}

class notifieur_touche_clavier {
	int tu_, ts_;
	bool r_;
public:
	notifieur_touche_clavier(int tu, int ts, bool r) : tu_(tu), ts_(ts), r_(r) {}
	void operator()(observateur_evenement *e) {
		e->touche_pressee(tu_, ts_, r_);
	}
};

void module_interface::notifier_touche_clavier(int tu, int ts, bool r) {
	std::for_each(a_notifier_.begin(), a_notifier_.end(), notifieur_touche_clavier(tu, ts, r));
}

class notifieur_souris_pressee {
	int x_, y_;
	bool r_;
public:
	notifieur_souris_pressee(int x, int y, bool r) : x_(x), y_(y), r_(r) {}
	void operator()(observateur_evenement *e) {
		e->souris_cliquee(x_, y_, r_);
	}
};

void module_interface::notifier_souris(int x, int y, bool r) {
	std::for_each(a_notifier_.begin(), a_notifier_.end(), notifieur_souris_pressee(x, y, r));
}

void module_interface::traiter_evenements() {
	SDL_Event e;
	if(SDL_PollEvent(&e))
		if(e.type == SDL_QUIT)
			demander_quitter();
		else if(e.type == SDL_KEYDOWN || e.type == SDL_KEYUP)
			notifier_touche_clavier(e.key.keysym.unicode, e.key.keysym.sym, e.type == SDL_KEYUP);
		else if(e.type == SDL_MOUSEBUTTONUP || e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT)
			notifier_souris(e.button.x, e.button.y, e.button.state == SDL_RELEASED);
}
